package com.tangyuan.common.util;

import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IpUtils {

    /**
     * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址,
     * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
     * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串。
     *
     * @param request 请求
     * @return IP地址
     */
    private static final Pattern PRIVATE_IPV4_PATTERN = Pattern.compile(
            "(^127\\.)|(10\\.)|(172\\.(1[6-9]|2[0-9]|3[0-1])\\.)|(192\\.168\\.)"
    );
    private static final Pattern PRIVATE_IPV6_PATTERN = Pattern.compile(
            "^([0-9a-fA-F]{1,4}:){7}([0-9a-fA-F]{1,4}|:)$"
    );
    private static final String[] HEADER_CANDIDATES = {
            "X-Forwarded-For", "X-Real-IP", "Proxy-Client-IP",
            "WL-Proxy-Client-IP", "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"
    };

    public static String getRealIp(HttpServletRequest request) {
        for (String header : HEADER_CANDIDATES) {
            String ip = request.getHeader(header);
            if (ip != null && !ip.isEmpty() && !"unknown".equalsIgnoreCase(ip)) {
                ip = extractFirstNonPrivateIp(ip);
                if (ip != null) {
                    return ip;
                }
            }
        }
        return request.getRemoteAddr();
    }

    private static String extractFirstNonPrivateIp(String ipList) {
        String[] ips = ipList.split(",");
        for (int i = ips.length - 1; i >= 0; i--) {
            String ip = ips[i].trim();
            if (!isPrivateOrLoopback(ip)) {
                return ip;
            }
        }
        return null;
    }

    private static boolean isPrivateOrLoopback(String ip) {
        if (ip.contains(":")) { // IPv6
            Matcher matcher = PRIVATE_IPV6_PATTERN.matcher(ip);
            return matcher.matches() || ip.equals("::1");
        } else { // IPv4
            Matcher matcher = PRIVATE_IPV4_PATTERN.matcher(ip);
            return matcher.matches() || ip.equals("127.0.0.1");
        }
    }
}